#include <Client.h>
#include <iostream>
#include <Threading.h>
using namespace std;

int main()
{ 
	WindowsThreading Obj;
	
	/** setting the values **/
	Obj.SetRowsMatrixA(2000);
	Obj.SetColumsMatrixA(2000);	
	Obj.SetRowsMatrixB(2000);
	Obj.SetColumsMatrixB(2000);
	if(Obj.GetColumsMatrixA() != Obj.GetRowsMatrixB())
	{
		cerr<<"ERROR: Multiplication not possible"<<endl;
		return 0;
	}
	/** Dynamically Assigning memory **/
	Obj.AssignMemory(MATRIXA);
	Obj.AssignMemory(MATRIXB);
	//Obj[0].AssignMemory(MATRIXC);

	/** Setting Matrix A and B with Random values **/
	Obj.MatrixGeneration(MATRIXA);
	Obj.MatrixGeneration(MATRIXB);

	Client client_obj; 		// Create Client object.

	client_obj.CreateSocket(TCPSOCKET);	// Create Client Socket.
	client_obj.SetSocketOptions();

	client_obj.InitialiseAddress(DEFAULTCLIENTPORT);	// Without any argument default port will be chosen.
	client_obj.Bind();

	char server_name[24];
	int server_port;
	cout << "Enter Server name or IP: "; // Use localhost or 127.0.0.1 for local server.
	cin.getline(server_name, 24);
	cout << "Enter Server port: ";
	cin >> server_port;

	// Connect to Server. Server name/IP and port are provided as arguments.
	client_obj.Connect(server_name, server_port);

	int matrix_A_rows,matrix_A_colums,matrix_B_rows,matrix_B_colums;
	int matrixA_length,matrixB_length;
	matrix_A_rows	= Obj.GetRowsMatrixA();
	matrix_A_colums = Obj.GetColumsMatrixA();
	matrix_B_rows	= Obj.GetRowsMatrixB();
	matrix_B_colums = Obj.GetColumsMatrixB();
	matrixA_length	= Obj.GetColumsMatrixA() * Obj.GetRowsMatrixA() * sizeof(int);
	matrixB_length	= Obj.GetColumsMatrixB() * Obj.GetRowsMatrixB() * sizeof(int);
	unsigned int total_bytes = 4*sizeof(int)+matrixA_length+matrixB_length;

	/** sending **/
	char *buffer = new char[total_bytes];
	Obj.MemoryCopy(buffer, &matrix_A_rows, sizeof(int));
	Obj.MemoryCopy(buffer+sizeof(int), &matrix_A_colums, sizeof(int));
	Obj.MemoryCopy(buffer+2*sizeof(int), &matrix_B_rows, sizeof(int));
	Obj.MemoryCopy(buffer+3*sizeof(int), &matrix_B_colums, sizeof(int));
	Obj.MemoryCopy(buffer+4*sizeof(int), Obj.GetMatrixA(), matrixA_length);
	Obj.MemoryCopy(buffer+4*sizeof(int)+matrixA_length, Obj.GetMatrixB(), matrixB_length);

	/**Encryption**/
	unsigned char keydata[] = "nucesfast";
	char *Encrypted = Encrypt(keydata, buffer, total_bytes);
	cout<<"Sending"<<endl;

	int sent_bytes = 0;
	unsigned char buffer_size[sizeof(int)];
	Obj.MemoryCopy(buffer_size, &total_bytes, sizeof(int));
	client_obj.Send((void*)&buffer_size,sizeof(int));
	client_obj.Receive();
	char* reply	= client_obj.GetBuffer();
	while(true)
	{
		if(client_obj.GetNumOfBytesSent() == -1 || sent_bytes>=total_bytes)
			break;
		
		client_obj.Send((void*)(Encrypted),total_bytes);//sending matrix 
		sent_bytes += client_obj.GetNumOfBytesSent();
	}

	cout<<"Done sending"<<endl<<endl<<"Recieving"<<endl;
	
	/** Recieving **/
		int matrixC_length=Obj.GetColumsMatrixB() * Obj.GetRowsMatrixA() * sizeof(int);
	int* recived_bufferj	= new int[matrixC_length/4];
	int* recived_bufferi	= new int[matrixC_length/4];
	int recived_bytes =	0;
	while(true)
	{
		if(client_obj.GetNumOfBytesReceived()==-1 || recived_bytes>=matrixC_length)
			break;

		client_obj.Receive((void*)(recived_bufferi),(int)matrixC_length);
		Obj.MemoryCopy(recived_bufferj + recived_bytes, recived_bufferi, client_obj.GetNumOfBytesReceived());
		recived_bytes += client_obj.GetNumOfBytesReceived();
	}
	char *Decrypted = Encrypt(keydata, (char*)recived_bufferj, matrixC_length);
	int* recived_Matrix_C	= (int*)Decrypted;
	Obj.SetMatrixC( recived_Matrix_C );
	cout<<"Done Recieving"<<endl<<"Storing"<<endl;
	/** Storing to file just for checking**/
	Obj.MatrixStore(MATRIXA);
	Obj.MatrixStore(MATRIXB);
	Obj.MatrixStore(MATRIXC);
	cout<<"Done Storing"<<endl<<"Deleting Memory"<<endl;
	/** Deleting allocated memory **/
	Obj.DeleteMemory(MATRIXA);
	Obj.DeleteMemory(MATRIXB);
	delete buffer;
	delete recived_Matrix_C;
	delete	recived_bufferi;
	cout<<"Done Deleting"<<endl;
	client_obj.CloseClientSocket();
	return 0;
}
